<template>
    <div class="attachments-uploader">
      <!-- 仅按钮触发文件选择：不显示拖拽/文件列表 -->
      <el-upload
        ref="uploadRef"
        :multiple="multiple"
        :auto-upload="false"
        :show-file-list="false"
        :on-change="onUploadChange"
        :before-upload="beforeUpload"
        :http-request="noop"
        :accept="accept"
      >
        <el-button type="primary" size="small">
          <el-icon><UploadFilled /></el-icon>
          选择文件
        </el-button>
  
        <span class="text-12 text-gray ml8" v-if="pendingFileList.length">
          已选择 {{ pendingFileList.length }} 个待上传
        </span>
        <el-button
          v-if="pendingFileList.length"
          link
          size="small"
          type="danger"
          class="ml8"
          @click="clearPending"
        >
          清空选择
        </el-button>
      </el-upload>
  
      <!-- 选择文件夹（可配置开关） -->
      <div v-if="directory" class="mt8">
        <el-button size="small" @click="pickDirectory">选择文件夹</el-button>
        <input
          ref="dirPickerRef"
          type="file"
          style="display:none"
          webkitdirectory
          directory
          multiple
          @change="onDirectoryPicked"
        />
        <div class="text-12 text-gray mt4">文件夹上传将保留相对路径（relativePath）</div>
      </div>
  
      <!-- 已有附件列表（编辑态） -->
      <el-table
        v-if="showTable && serverRows.length"
        :data="serverRows"
        size="small"
        class="mt8"
      >
        <el-table-column prop="fileName" label="文件名" min-width="220" />
        <el-table-column prop="relativePath" label="相对路径" min-width="200" />
        <el-table-column prop="fileSize" label="大小(B)" width="120" />
        <el-table-column label="操作" width="160">
          <template #default="scope">
            <el-button link type="primary" @click="previewAttachment(scope.row)">预览/下载</el-button>
            <el-button link type="danger"  @click="removeAttachment(scope.row)">删除</el-button>
          </template>
        </el-table-column>
      </el-table>
    </div>
  </template>
  
  <script setup>
  import { ref, watch, getCurrentInstance } from 'vue'
  import { UploadFilled } from '@element-plus/icons-vue'
  
  /**
   * 按钮版附件上传组件
   * - 父组件提供：listFn(entityId)、uploadFn(entityId, FormData)、deleteFn(attachmentId)
   * - 对外方法：reset/refresh/uploadAll/getPendingFiles/clearPending
   * - 支持目录选择并保留 webkitRelativePath -> relativePath
   */
  const props = defineProps({
    /** 业务实体 ID（有则自动回显附件，没有则仅收集待上传） */
    entityId: { type: [String, Number], default: null },
  
    /** API：查询已关联附件 listFn(entityId) -> { rows:[{id,fileName,fileUrl,fileSize,relativePath}] } | rows[] */
    listFn: { type: Function, required: true },
  
    /** API：上传 uploadFn(entityId, FormData(files[], relativePaths[])) */
    uploadFn: { type: Function, required: true },
  
    /** API：删除 deleteFn(attachmentId) */
    deleteFn: { type: Function, required: true },
  
    /** 可选外观/限制 */
    accept: { type: String, default: '.png,.jpg,.jpeg,.webp,.gif,.pdf,.doc,.docx,.xls,.xlsx,.ppt,.pptx,.txt,.zip,.rar' },
    maxSizeMB: { type: Number, default: 100 },
    multiple: { type: Boolean, default: true },
    directory: { type: Boolean, default: true },
    showTable: { type: Boolean, default: true },
  })
  
  const emit = defineEmits(['change', 'uploaded', 'removed', 'error', 'ready'])
  
  const uploadRef = ref()
  const dirPickerRef = ref()
  
  // 服务器已有（表格数据）
  const serverRows = ref([])
  
  // 本次新增、待上传（仅本地）
  const pendingFileList = ref([])
  
  watch(() => props.entityId, async (id) => {
    if (id) await refresh()
  }, { immediate: true })
  
  /** 对外：刷新回显 */
  async function refresh() {
    try {
      const resp = await props.listFn(props.entityId)
      const list = resp?.rows || resp?.data || resp || []
      serverRows.value = list
      emit('ready', { count: serverRows.value.length })
    } catch (e) {
      emit('error', e)
    }
  }
  
  /** 对外：仅清空“待上传” */
  function reset() {
    pendingFileList.value = []
  }
  
  /** 对外：获取待上传文件 */
  function getPendingFiles() {
    return pendingFileList.value.slice()
  }
  
  /** 对外：执行上传（需传入 entityId；常见做法是父组件在新增获得 id 后再调） */
  async function uploadAll(entityIdArg) {
    const targetId = entityIdArg ?? props.entityId
    if (!targetId) return { ok: true, uploaded: 0 }
  
    if (!pendingFileList.value.length) return { ok: true, uploaded: 0 }
  
    const fd = new FormData()
    let count = 0
    for (const f of pendingFileList.value) {
      const raw = f.raw || f
      if (!beforeUpload(raw)) continue
      fd.append('files', raw)
      const relative = f.relativePath || raw.webkitRelativePath || ''
      fd.append('relativePaths', relative)
      count++
    }
    if (count === 0) return { ok: true, uploaded: 0 }
  
    await props.uploadFn(targetId, fd)
    pendingFileList.value = []
    await refresh()
    emit('uploaded', { count })
    return { ok: true, uploaded: count }
  }
  
  /** —— el-upload 相关 —— */
  const noop = () => {}
  
  function onUploadChange(_uploadFile, uploadFiles) {
    // 仅保留本地新增（el-upload 返回的 file 对象里本地文件在 raw 字段）
    pendingFileList.value = (uploadFiles || []).map(f => ({
      name: f.name,
      status: f.status || 'ready',
      size: f.size,
      raw: f.raw || f,
      uid: f.uid || `${Date.now()}_${Math.random()}`,
      relativePath: f.relativePath || f.raw?.webkitRelativePath || ''
    }))
    emit('change', { pending: pendingFileList.value.length })
  }
  
  function clearPending() {
    pendingFileList.value = []
    emit('change', { pending: 0 })
  }
  
  function beforeUpload(file) {
    const isLt = file.size / 1024 / 1024 < props.maxSizeMB
    if (!isLt) {
      getCurrentInstance()?.proxy?.$modal?.msgError?.(`文件 ${file.name} 超过 ${props.maxSizeMB}MB`)
      return false
    }
    return true
  }
  
  /** 目录选择（保留相对路径） */
  function pickDirectory() {
    dirPickerRef.value && dirPickerRef.value.click()
  }
  function onDirectoryPicked(e) {
    const files = Array.from(e.target.files || [])
    const toPush = files.map((raw, idx) => ({
      name: raw.name,
      status: 'ready',
      size: raw.size,
      raw,
      uid: `${Date.now()}_${idx}`,
      relativePath: raw.webkitRelativePath || ''
    }))
    pendingFileList.value = pendingFileList.value.concat(toPush)
    e.target.value = ''
    emit('change', { pending: pendingFileList.value.length })
  }
  
  /** 工具：相对路径转绝对 URL（兼容 RuoYi 的 BASE_API 前缀） */
  function makeAbsUrl(path) {
    if (!path) return ''
    if (/^https?:\/\//i.test(path)) return path
    const BASE = import.meta.env?.VITE_APP_BASE_API || process.env?.VUE_APP_BASE_API || ''
    return `${BASE}${path}`
  }
  
  /** 表格操作（预览/删除已有） */
  function previewAttachment(row) {
    window.open(makeAbsUrl(row.fileUrl), '_blank')
  }
  async function removeAttachment(row) {
    await props.deleteFn(row.id)
    await refresh()
    emit('removed', { id: row.id, server: true })
  }
  
  defineExpose({ reset, refresh, uploadAll, getPendingFiles, clearPending })
  </script>
  
  <style scoped>
  .mt4 { margin-top: 4px; }
  .mt8 { margin-top: 8px; }
  .ml8 { margin-left: 8px; }
  .text-12 { font-size: 12px; }
  .text-gray { color: #909399; }
  </style>
  